﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;

namespace RoketPack.IM
{
    class NotificationManager
    {
        private List<NotificationForm> m_Windows = new List<NotificationForm>();
        private List<string> m_Notifications = new List<string>();
        private Thread m_Thread = null;
        private Screen m_Screen = null;
        private object m_Lock = new object();
        private bool m_Running = true;

        /// <summary>
        /// Creates a new instance of NotificationManager, which accepts string
        /// messages and displays them to the user in an unobtrusive way.
        /// </summary>
        /// <param name="screen">The screen on which the notifications should be displayed.</param>
        internal NotificationManager(Screen screen)
        {
            this.m_Screen = screen;

            this.m_Thread = new Thread(this.Process);
            this.m_Thread.Name = "IM Notification Processor";
            this.m_Thread.IsBackground = true;
            this.m_Thread.Start();
        }

        /// <summary>
        /// The destructor for a NotificationManager, which simply cleans up
        /// the thread.
        /// </summary>
        ~NotificationManager()
        {
            if (this.m_Thread.IsAlive)
                this.m_Thread.Abort();
        }

        /// <summary>
        /// Adds a new message to the notification manager, displaying it to the user.
        /// </summary>
        /// <param name="msg">The text message.</param>
        internal void Add(string msg)
        {
            lock (this.m_Lock)
            {
                if (msg == null)
                    return;

                this.m_Notifications.Add(msg);
            }
        }

        /// <summary>
        /// Clears all of the current notification windows from the screen.
        /// </summary>
        internal void Clear()
        {
            List<NotificationForm> tmp = new List<NotificationForm>();
            foreach (NotificationForm f in this.m_Windows)
                tmp.Add(f);

            foreach (NotificationForm f in tmp)
            {
                RoketPack.Manager.VoidLambda l = () =>
                {
                    f.Close();
                };

                try
                {
                    if (f.InvokeRequired)
                        f.Invoke(l);
                    else
                        l();
                }
                catch (ObjectDisposedException) { }
                catch (InvalidOperationException) { }
            }
        }

        /// <summary>
        /// Terminates the thread and closes all of the notification windows.
        /// </summary>
        internal void Close()
        {
            this.m_Running = false;
            while (this.m_Thread.IsAlive) ;

            this.Clear();
        }

        /// <summary>
        /// Processes the notification message queue, managing the display and
        /// location of the notification windows.
        /// </summary>
        private void Process()
        {
            while (this.m_Running)
            {
                lock (this.m_Lock)
                {
                    // Process all the new notifications
                    foreach (string s in this.m_Notifications)
                    {
                        // Add a new window on top of the stack
                        bool complete = false;
                        Thread t = new Thread(() =>
                        {
                            NotificationForm w = new NotificationForm(s);
                            w.Load += (sender, e) =>
                            {
                            };
                            w.FormClosed += (sender, e) =>
                            {
                                this.m_Windows.Remove(w);
                            };
                            IntPtr hwnd = w.Handle; // Force generation of the window handle.
                            this.m_Windows.Add(w);
                            complete = true;
                            Application.Run(w);
                        });
                        t.Name = "IM Notification";
                        t.IsBackground = true;
                        t.Start();

                        // We have to wait until the form has been added to the dictionary.
                        while (!complete) ;
                    }
                    this.m_Notifications.Clear();

                    // Update all the positioning.
                    try
                    {
                        int i = 0;
                        foreach (NotificationForm f in this.m_Windows)
                        {
                            RoketPack.Manager.VoidLambda l = () =>
                                {
                                    try
                                    {
                                        i += f.UpdatePositioning(this.m_Screen, i);
                                    }
                                    catch (ObjectDisposedException) { }
                                };

                            try
                            {
                                if (f.InvokeRequired)
                                    f.Invoke(l);
                                else
                                    l();
                            }
                            catch (ObjectDisposedException) { }
                        }
                        foreach (NotificationForm f in this.m_Windows)
                        {
                            RoketPack.Manager.VoidLambda l = () =>
                            {
                                try
                                {
                                    if (f.Visible == false)
                                        f.ShowInactiveTopmost();
                                }
                                catch (ObjectDisposedException) { }
                            };

                            try
                            {
                                if (f.InvokeRequired)
                                    f.Invoke(l);
                                else
                                    l();
                            }
                            catch (ObjectDisposedException) { }
                        }
                    }
                    catch (InvalidOperationException)
                    {
                    }
                }

                // Sleep for 10 milliseconds.
                Thread.Sleep(10);
            }
        }
    }
}
